/** <a href="http://www.cpupk.com/decompiler">Eclipse Class Decompiler</a> plugin, Copyright (c) 2017 Chen Chao. **/
package com.asiainfo.utils.sql.sqlbuilder.select;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.asiainfo.utils.sql.sqlbuilder.Database;

public class Context {
	private final Connection connection;
	private final StringBuilder sql;
	private final List<Object> parameters;
	private final Database database;
	private final transient Logger log = Logger.getLogger(super.getClass().getName());

	Context(Context clone) {
		this.connection = clone.connection;
		this.parameters = new LinkedList(clone.parameters);
		this.sql = new StringBuilder();
		this.database = clone.database;
	}

	Context(Database database, Connection connection) {
		this.connection = connection;
		this.database = database;
		this.sql = new StringBuilder();
		this.parameters = new LinkedList();
	}

	public String toString() {
		return getSql();
	}

	<E> List<E> list(RowMapper<E> rowMapper) throws SQLException {
		List result = new LinkedList();
		ResultSet resultSet = query(getSql());
		int rowNum = 0;
		while (resultSet.next()) {
			Object obj = rowMapper.convert(resultSet, rowNum++);
			result.add(obj);
		}

		return result;
	}

	<E> E single(RowMapper<E> rowMapper) throws SQLException {
		E result = null;
		ResultSet resultSet = query(getSql());
		if (resultSet.next()) {
			result = rowMapper.convert(resultSet, 1);
			if (resultSet.next()) {
				throw new SQLException("The query returned more than one result");
			}
		}
		return result;
	}

	public HashMap single() throws SQLException {
		return getRow(query(getSafeSql()));
	}

	List<HashMap> list() throws SQLException {
		return getRows(query(getSafeSql()));
	}

	void append(String expression) {
		this.sql.append(expression);
		this.sql.append("\n");
	}

	void addParameters(Object[] parameters) {
		for (Object parameter : parameters)
			this.parameters.add(parameter);
	}

	Database getDatabase() {
		return this.database;
	}

	ResultSet query(String sql) throws SQLException {
		this.log.log(Level.INFO, "\n" + sql);
		ResultSet rs = null;
		PreparedStatement ps = null;
		ps = prepareStatement(sql);
		rs = ps.executeQuery();
		return rs;
	}

	boolean execute(String sql) throws SQLException {
		this.log.log(Level.INFO, "\n" + sql);
		PreparedStatement ps = null;
		ps = prepareStatement(sql);
		return ps.execute();
	}

	String getSql() {
		return this.sql.toString();
	}

	String getSafeSql() throws SQLException {
		String sql = prepareStatement(toString()).toString();
		return sql.substring(sql.indexOf(":") + 2);
	}

	PreparedStatement prepareStatement(String sql) throws SQLException {
		PreparedStatement statement = this.connection.prepareStatement(sql);
		int i = 1;
		int pcount = statement.getParameterMetaData().getParameterCount();
		for (Iterator localIterator = this.parameters.iterator(); localIterator.hasNext();) {
			Object parameter = localIterator.next();
			if (i > pcount) {
				continue;
			}
			statement.setObject(i++, parameter);
		}
		return statement;
	}

	List<HashMap> getRows(ResultSet rs) throws SQLException {
		ArrayList ret = new ArrayList();
		ArrayList rsColNames = new ArrayList();
		if (rs != null) {
			while (rs.next()) {
				rsColNames = getColumns(rs);

				Map line = new HashMap();
				for (int i = 0; i < rsColNames.size(); ++i) {
					line.put(rsColNames.get(i).toString(), rs.getObject(rsColNames.get(i).toString()));
				}
				ret.add(line);
			}
			try {
				rs.close();
			} catch (Exception e) {
			}
			return ret;
		}
		return null;
	}

	HashMap getRow(ResultSet rs) throws SQLException {
		ArrayList rsColNames = new ArrayList();
		if ((rs != null) && (rs.next())) {
			rsColNames = getColumns(rs);
			HashMap line = new HashMap();
			for (int i = 0; i < rsColNames.size(); ++i)
				line.put(rsColNames.get(i).toString(), rs.getObject(rsColNames.get(i).toString()));
			try {
				rs.close();
			} catch (Exception e) {
			}
			return line;
		}

		return null;
	}

	ArrayList getColumns(ResultSet rs) throws SQLException {
		ArrayList ret = new ArrayList();
		ResultSetMetaData rsmd = rs.getMetaData();

		int k = rsmd.getColumnCount();
		for (int i = 1; i <= k; ++i) {
			ret.add(rsmd.getColumnName(i));
		}
		return ret;
	}

	public List getTables() throws SQLException {
		DatabaseMetaData metaData = this.connection.getMetaData();
		ResultSet rs = metaData.getTables(null, null, null, new String[]{"TABLE"});
		List tableNameList = new ArrayList();
		while (rs.next()) {
			tableNameList.add(rs.getString("TABLE_NAME"));
		}
		return tableNameList;
	}

	void close() {
		try {
			this.connection.close();
		} catch (Exception e) {
		}
	}
}